// 赢者为王
// 采用并发编程的动机有很多：并发编程可以简化问题，比如一类问题对应一个处理线程会更简单；并发编程还可以提升性能，在一个多核CPU上开2个线程一般会比开1个线程快一些。其实对于提升性能而言，程序并不是简单地运行速度快就表示用户体验好的；很多时候程序能快速响应用户请求才是最重要的，当没有用户请求需要处理的时候才合适处理一些低优先级的后台任务。

// 假设我们想快速地检索“golang”相关的主题，我们可能会同时打开Bing、Google或百度等多个检索引擎。当某个检索最先返回结果后，就可以关闭其它检索页面了。因为受限于网络环境和检索引擎算法的影响，某些检索引擎可能很快返回检索结果，某些检索引擎也可能遇到等到他们公司倒闭也没有完成检索的情况。我们可以采用类似的策略来编写这个程序：

// func main() {
// 	ch := make(chan string, 32)

// 	go func() {
// 		ch <- searchByBing("golang")
// 	}
// 	go func() {
// 		ch <- searchByGoogle("golang")
// 	}
// 	go func() {
// 		ch <- searchByBaidu("golang")
// 	}

// 	fmt.Println(<-ch)
// }
// 首先，我们创建了一个带缓存的管道，管道的缓存数目要足够大，保证不会因为缓存的容量引起不必要的阻塞。然后我们开启了多个后台线程，分别向不同的检索引擎提交检索请求。当任意一个检索引擎最先有结果之后，都会马上将结果发到管道中（因为管道带了足够的缓存，这个过程不会阻塞）。但是最终我们只从管道取第一个结果，也就是最先返回的结果。

// 通过适当开启一些冗余的线程，尝试用不同途径去解决同样的问题，最终以赢者为王的方式提升了程序的相应性能。
// ===============================================================================================
// 很多用户在适应了Go语言强大的并发特性之后，都倾向于编写最大并发的程序，
// 因为这样似乎可以提供最大的性能。在现实中我们行色匆匆，但有时却需要我们放慢脚步享受生活，
// 并发的程序也是一样：有时候我们需要适当地控制并发的程度，因为这样不仅仅可给其它的应用/任务让出/预留一定的CPU资源，
// 也可以适当降低功耗缓解电池的压力。

// 在Go语言自带的godoc程序实现中有一个vfs的包对应虚拟的文件系统，在vfs包下面有一个gatefs的子包，gatefs子包的目的就是为了控制访问该虚拟文件系统的最大并发数。gatefs包的应用很简单：

// import (
// 	"golang.org/x/tools/godoc/vfs"
// 	"golang.org/x/tools/godoc/vfs/gatefs"
// )

// func main() {
// 	fs := gatefs.New(vfs.OS("/path"), make(chan bool, 8))
// 	// ...
// }
// 其中vfs.OS("/path")基于本地文件系统构造一个虚拟的文件系统，
// 然后gatefs.New基于现有的虚拟文件系统构造一个并发受控的虚拟文件系统。
// 并发数控制的原理在前面一节已经讲过，就是通过带缓存管道的发送和接收规则来实现最大并发阻塞：

package main

import (
	"fmt"
	"sync"
	"time"
)

func timeTest() {
	for {
		select {
		case v := <-time.After(time.Second * 20):
			// 有种类似time 定时器的样子<track>
			// 返回的是一个时间管道，20内是堵塞的里面没有数据，20的时候，会让管道里面写入时间
			fmt.Println(v)
		}
	}
}
func worker(cannel chan bool) {
	for {
		select {
		default:
			fmt.Println("hello")
			// 正常工作
		case <-cannel:
			// 退出
		}
	}
}

var num = make(chan int)

func randomNum() {
	for {
		select {
		case num <- 0:
		case num <- 1:
		}
	}
}

func worker1(wg *sync.WaitGroup, cannel chan bool) {
	defer wg.Done()

	for {
		select {
		default:
			fmt.Println("hello")
		case <-cannel:
			return
		}
	}
}
func main() {

	// go randomNum()
	// for v := range num {
	// 	fmt.Println(v)
	// }
	// -----------------------------------------------

	// 我们通过close来关闭cancel管道向多个Goroutine广播退出的指令。
	// 不过这个程序依然不够稳健：当每个Goroutine收到退出指令退出时一般会进行一定的清理工作，但是退出的清理工作并不能保证被完成，
	// 因为main线程并没有等待各个工作Goroutine退出工作完成的机制。我们可以结合sync.WaitGroup来改进:
	// cancel := make(chan bool)

	// for i := 0; i < 10; i++ {
	// 	go worker(cancel)
	// }

	// time.Sleep(time.Second)
	// close(cancel)
	// -----------------------------------------------
	cancel := make(chan bool)

	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go worker1(&wg, cancel)
	}

	time.Sleep(time.Second)
	close(cancel)
	wg.Wait()
}

// channel 优雅退出的方法，
// select 来实现，通过时间来控制
// select 通过case 对应channel的值来控制
// select 通过default 来做优化并发循环的时间,减少CPU的资源性能消耗
